﻿using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.Geodatabase;
using System.Collections.Specialized;
using System.Diagnostics;

namespace CanSWAT.ArcSWAT
{
    /// <summary>
    /// corresponding to feature class FullHRU
    /// </summary>
    class HRU
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fea">A feature in FullHRU</param>
        /// <param name="sub">Corresponding subbasin object</param>
        /// <param name="splitLanduse">The landuse split information to generate sub hru</param>
        /// <param name="modeledHRUs">All the codes of HRUs modeled in the final model</param>
        public HRU(IFeature fea, Subbasin sub,
            Dictionary<string, Landuse> splitLanduse, StringCollection modeledHRUs)
        {
            _id = fea.OID;
            _landuse = fea.get_Value(LANDUSE_NAME_INDEX).ToString();
            _soil= fea.get_Value(SOIL_NAME_INDEX).ToString();
            _slope = fea.get_Value(SLOPE_NAME_INDEX).ToString();
            _area = Convert.ToDouble(fea.get_Value(AREA_INDEX).ToString());

            _slope_along_wind = 0.0;
            _curvature_along_wind = 0.0;

            int index = fea.Fields.FindField(ProjectDatabase.FIELD_NAME_SLOPE_ALONG_WIND);
            if (index > -1) _slope_along_wind = Convert.ToDouble(fea.get_Value(index).ToString());

            index = fea.Fields.FindField(ProjectDatabase.FIELD_NAME_CURVATURE_ALONG_WIND);
            if (index > -1) _curvature_along_wind = Convert.ToDouble(fea.get_Value(index).ToString());

            _slope_along_wind_modeld = 0.0;
            _curvature_along_wind_modeld = 0.0;

            _subbasin = sub;
            _subbasin.AddHRU(this);
            if (splitLanduse != null && splitLanduse.ContainsKey(_landuse))
            {
                Landuse split = splitLanduse[_landuse];
                _subHRU = split.GenerateSubHRUs(this,modeledHRUs);  //create all the sub hrus                
            }

            createUniqueCombinationCode();
            updateModeledFlag(modeledHRUs);            
        }

        /// <summary>
        /// used to create sub hru
        /// </summary>
        /// <param name="landuse"></param>
        /// <param name="parentHRU"></param>
        public HRU(Landuse subLanduse, HRU parentHRU, StringCollection modeledHRUs)
        {
            _id = -1;
            _landuse = subLanduse.Code;
            _soil = parentHRU.Soil;
            _slope = parentHRU.Slope;
            _area = parentHRU.Area * subLanduse.Percentage / 100.0; //calculate the area

            _slope_along_wind = parentHRU.SlopeAlongWind;
            _curvature_along_wind = parentHRU.CurvatureAlongWind;
            
            _subbasin = parentHRU.Subbasin;

            createUniqueCombinationCode();
            updateModeledFlag(modeledHRUs);    
        }

        private void updateModeledFlag(StringCollection modeledHRUs)
        {
            if (_subHRU == null)
                _isModeled = modeledHRUs.Contains(this._code);
            else
            {
                foreach (HRU hru in _subHRU)
                    if (hru.Modeled)
                    {
                        _isModeled = true;
                        break;
                    }
            }
        }

        public static int SUBBAIN_INDEX = 5;
        private static int LANDUSE_NAME_INDEX = 7;
        private static int SOIL_NAME_INDEX = 9;
        private static int SLOPE_NAME_INDEX = 11;
        private static int AREA_INDEX = 13;

        private int _id;
        private Subbasin _subbasin;
        private string _landuse;
        private string _soil;
        private string _slope;
        private string _code; //this is the unique code for hru, 1_FYDL_C_0-9999
        private double _area;

        //this is the flag for if the hru is modeled in the model
        //only slope and curvature of modeled hrus needs to be calculated.
        private bool _isModeled = false;

        //if current HRU is split into sub landuses
        private List<HRU> _subHRU = null;

        private double _slope_along_wind;
        private double _curvature_along_wind;

        private double _slope_along_wind_modeld;
        private double _curvature_along_wind_modeld;

        private void createUniqueCombinationCode()
        {
            _code = string.Format("{0}_{1}_{2}_{3}",_subbasin.ID,_landuse,_soil,_slope);
        }

        public string Landuse
        {
            get { return _landuse; }
        }

        public string Soil
        {
            get { return _soil; }
        }

        public string Slope
        {
            get { return _slope; }
        }

        public double Area
        {
            get { return _area; }
        }

        public double SlopeAlongWind
        {
            get { return _slope_along_wind; }
            set { _slope_along_wind = value; }
        }

        public double CurvatureAlongWind
        {
            get { return _curvature_along_wind; }
            set { _curvature_along_wind = value; }
        }

        public double SlopeAlongWind_Modeled
        {
            get { return _slope_along_wind_modeld; }
        }

        public double CurvatureAlongWind_Modeled
        {
            get { return _curvature_along_wind_modeld; }
        }

        public Subbasin Subbasin
        {
            get { return _subbasin; }
        }

        public bool Modeled
        {
            get { return _isModeled; }
        }

        public void saveParameters(ITable t)
        {
            if (!Modeled) return;

            if(_subHRU == null)
                saveParameters(t, _slope_along_wind_modeld, _curvature_along_wind, 0.0);
            else
                foreach(HRU subhru in _subHRU)
                    subhru.saveParameters(t, _slope_along_wind_modeld, _curvature_along_wind, 0.0);
        }

        public void saveParameters(ITable t, double newslope, double newcurvature, double newshc)
        {
            if (!Modeled) return;

            //write into database
            IQueryFilter qf = new QueryFilterClass();
            qf.WhereClause = string.Format("{0} = '{1}'", ProjectDatabase.FIELD_NAME_HRUS_UNIAQUECOMB, _code);
            ICursor updateCursor = t.Update(qf, false);
            try
            {
                IRow r = updateCursor.NextRow();

                if (r == null)
                {
                    Debug.WriteLine("Warning: Can't find HRU " + _code + " in table hrus.");
                    return;
                }

                int slope_index = r.Fields.FindField(ProjectDatabase.FIELD_NAME_SLOPE_ALONG_WIND);
                int curva_index = r.Fields.FindField(ProjectDatabase.FIELD_NAME_CURVATURE_ALONG_WIND);

                r.set_Value(slope_index, newslope);
                r.set_Value(curva_index, newcurvature);
                updateCursor.UpdateRow(r);
            }
            finally
            {
                System.Runtime.InteropServices.Marshal.ReleaseComObject(updateCursor);
            }      
        }

        /// <summary>
        /// re-calculate slope, curvature and SHC using area weighted method. This is the core of the algorithm.
        /// </summary>
        /// <param name="hrusInSameSubbasin">All HRUs in the same Subbasin</param>
        /// <param name="modeldHRUAreaInCurrentSubbasin">The total area of all modeled HRUs in the same Subbasin</param>
        public void reCalParameters()
        {
            if(!Modeled) return;
            if(Subbasin == null) return;

            double ratio = Area / Subbasin.Area_ModeldHRUs;
            double slope = SlopeAlongWind * Area;
            double curvature = CurvatureAlongWind * Area;
            double totalArea = Area;
            foreach (HRU hru in Subbasin.HRUs)
            {
                if (!hru.Modeled)
                {
                    double splitArea = hru.Area * ratio;
                    slope += splitArea * hru.SlopeAlongWind;
                    curvature += splitArea * hru.CurvatureAlongWind;

                    totalArea += splitArea;
                }
            }
            _slope_along_wind_modeld = slope / totalArea;
            _curvature_along_wind_modeld = curvature / totalArea;
        }
    }
}
